/*
 * @Author: zhangshu
 * @email: shiliang.zhang@tuya.com
 * @LastEditors: Please set LastEditors
 * @file name: app_gpio.c
 * @Description: 
 * @Copyright: HANGZHOU TUYA INFORMATION TECHNOLOGY CO.,LTD
 * @Company: http://www.tuya.com
 * @Date: 2022-02-08
 * @LastEditTime: 2022-02-08
 */
#include "tkl_system.h"
#include "tal_memory.h"
#include "tal_gpio.h"
#include "tal_sw_timer.h"
#include "tal_network_mgr.h"

#include "app_common.h"
#include "app_gpio.h"
#include "app_mfg.h"
#include "app_ias_zone.h"
#include "app_led.h"
#include "app_battery.h"
#include "tal_zll_commissioning.h"

#include "timer.h"

#define VIBRATE_CNT                       3
#define VIBRATE_TIME                      20
#define KEY_PUSH_TIME_SHORT               700

//sensor interrupt infomation
STATIC APP_GPIO_INFO_T *int_info;
STATIC UINT16_T alarm_cnt = 0;
STATIC UINT16_T button_cnt = 0;
STATIC UINT16_T scan_key_cnt = 0;
STATIC UINT16_T factory_new_key_cnt = 0;
STATIC TUYA_GPIO_LEVEL_E last_alarm_state = TUYA_GPIO_LEVEL_LOW;
STATIC BOOL_T is_in_alarm = FALSE;

//key push short times
STATIC BOOL_T key_press_flag = FALSE;

//all sensor interrupt event and handler
TIMER_ID etimer_alarm1_debounce_event_id;
TIMER_ID etimer_key_debounce_event_id;
TIMER_ID etimer_zll_scan_key_check_event_id;
TIMER_ID etimer_zll_factory_new_key_check_event_id;

STATIC VOID_T gpio_alarm1_debounce_handler(TIMER_ID timer_id, VOID_T *arg);
STATIC VOID_T gpio_key_debounce_handler(TIMER_ID timer_id, VOID_T *arg);
STATIC VOID_T gpio_zll_scan_key_deal_handler(TIMER_ID timer_id, VOID_T *arg);
STATIC VOID_T gpio_zll_factory_new_key_deal_handler(TIMER_ID timer_id, VOID_T *arg);

//app gpio event create
STATIC VOID_T app_gpio_event_init(VOID_T);

//gpio interrupt callback handler, register when config irq, please referance TUYA_GPIO_IRQ_CB
STATIC VOID_T gpio_sensor_alarm1_interrupt_handler(VOID_T *args);
STATIC VOID_T gpio_sensor_key_interrupt_handler(VOID_T *args);
STATIC VOID_T gpio_zll_scan_key_interrupt_handler(VOID_T *args);
STATIC VOID_T gpio_zll_factory_new_key_interrupt_handler(VOID_T *args);

STATIC VOID_T alarm_switch_alarm1_active(VOID_T);
STATIC VOID_T alarm_switch_alarm1_inactive(VOID_T);

STATIC VOID_T net_key_press_callback(UINT32_T time_ms);
STATIC VOID_T net_key_long_press_release_callback(UINT32_T time_ms);
STATIC VOID_T net_key_short_press_release_callback(UINT8_T count);
STATIC VOID_T gpio_key_interrupt_handler(UINT32_T key_id, UINT8_T key_st, UINT32_T push_time);

STATIC VOID_T hw_timer_zll_scan_key_deal_handler(VOID_T *arg);

/**
 * @description: app gpio interrupt init, include alarm tamper and trouble
 * @param[in] gpio_info: gpio info, please refer to APP_GPIO_INFO_T
 * @return: OPERATE_RET, success when OPRT_OK, or else
 */
OPERATE_RET app_gpio_oem_init(APP_GPIO_INFO_T *gpio_info)
{
    STATIC UINT8_T is_init = 0;

    if (is_init || gpio_info == NULL) {
        USER_PR_DEBUG("gpio already init or gpio info null\r\n");
        return OPRT_COM_ERROR;
    }

    int_info = (APP_GPIO_INFO_T *)tal_malloc(SIZEOF(APP_GPIO_INFO_T));

    if (int_info == NULL) {
        USER_PR_DEBUG("[malloc error] app gpio malloc error\r\n");
        return OPRT_MALLOC_FAILED;
    }

    is_init = 1;
    tal_system_memset(int_info, 0, SIZEOF(APP_GPIO_INFO_T));
    tal_system_memcpy(int_info, gpio_info, SIZEOF(APP_GPIO_INFO_T));

    TUYA_GPIO_IRQ_T gpio_irq;
    gpio_irq.mode = TUYA_GPIO_IRQ_RISE_FALL;

    if (int_info->alarm.enabled) {
        gpio_irq.cb = gpio_sensor_alarm1_interrupt_handler;

        tal_gpio_irq_init(int_info->alarm.pin_num, &gpio_irq);
    }
    if (int_info->key.enabled) {
        gpio_irq.cb = gpio_sensor_key_interrupt_handler;

        tal_gpio_irq_init(int_info->key.pin_num, &gpio_irq);
    }
    if (int_info->zll_factory_new_key.enabled) {
        gpio_irq.cb = gpio_zll_factory_new_key_interrupt_handler;

        tal_gpio_irq_init(int_info->zll_factory_new_key.pin_num, &gpio_irq);
    }    
    if (int_info->zll_scan_key.enabled) {
        gpio_irq.cb = gpio_zll_scan_key_interrupt_handler;

        tal_gpio_irq_init(int_info->zll_scan_key.pin_num, &gpio_irq);
    }
    USER_PR_DEBUG("int init OK!!!\r\n");
    return OPRT_OK;
}

/**
 * @description: gpio init after preheat time delay, gpio state may not stable during the preheat time
 * @param[in] none
 * @return: none
 */
VOID_T app_gpio_preheat_init(VOID_T)
{
    STATIC UINT8_T first_flag = 0;
    if (!first_flag) {
        first_flag = 1;

        TUYA_GPIO_LEVEL_E alarm_state = TUYA_GPIO_LEVEL_LOW;
        TUYA_GPIO_LEVEL_E key_state = TUYA_GPIO_LEVEL_LOW;
        tal_gpio_read(int_info->alarm.pin_num, &alarm_state);
        tal_gpio_read(int_info->key.pin_num, &key_state);
        USER_PR_DEBUG("alarm first state %d, key first state %d\r\n", alarm_state, key_state);

        last_alarm_state = alarm_state;
        if (alarm_state == (TUYA_GPIO_LEVEL_E)int_info->alarm.driver_flag) {
            alarm_switch_alarm1_active();
            is_in_alarm = TRUE;
        } else {
            alarm_switch_alarm1_inactive();
            is_in_alarm = FALSE;
        }

        app_gpio_event_init();

        if (!key_state) {
            tal_sw_timer_start(etimer_key_debounce_event_id, 0, TAL_TIMER_ONCE);
        }
    }
}

/**
 * @description: gpio event create
 * @param[in] none
 * @return: none
 */
STATIC VOID_T app_gpio_event_init(VOID_T)
{
    tal_sw_timer_create(gpio_alarm1_debounce_handler, NULL, &etimer_alarm1_debounce_event_id);
    tal_sw_timer_create(gpio_key_debounce_handler, NULL, &etimer_key_debounce_event_id);
    tal_sw_timer_create(gpio_zll_scan_key_deal_handler, NULL, &etimer_zll_scan_key_check_event_id);
    tal_sw_timer_create(gpio_zll_factory_new_key_deal_handler, NULL, &etimer_zll_factory_new_key_check_event_id);
}

/**
 * @description: irq callback, alarm interrupt handler
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T gpio_sensor_alarm1_interrupt_handler(VOID_T *args)
{
    USER_PR_DEBUG("into alarm1_interrupt\r\n");
    if (!alarm_cnt) {
        tal_sw_timer_start(etimer_alarm1_debounce_event_id, VIBRATE_TIME, TAL_TIMER_ONCE);
    }
}

/**
 * @description: irq callback, tamper interrupt handler
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T gpio_sensor_key_interrupt_handler(VOID_T *args)
{
    USER_PR_DEBUG("into key_interrupt\r\n");
    if (!button_cnt) {
        USER_PR_DEBUG("into etimer_key_debounce_event_id start\r\n");
        tal_sw_timer_start(etimer_key_debounce_event_id, VIBRATE_TIME, TAL_TIMER_ONCE);
    }
}


STATIC VOID_T gpio_zll_scan_key_interrupt_handler(VOID_T *args)
{
    USER_PR_DEBUG("into zll_scan_key_interrupt\r\n");
#if 1
    if (!button_cnt) {
        tal_sw_timer_start(etimer_zll_scan_key_check_event_id, VIBRATE_TIME, TAL_TIMER_ONCE);
    }
#else
    TUYA_GPIO_LEVEL_E state;
    tal_gpio_read(TUYA_GPIO_NUM_13, &state);
    USER_PR_DEBUG("into zll_scan_key_interrupt state %d\r\n",state);
    if(TUYA_GPIO_LEVEL_HIGH == state){
        TUYA_TIMER_BASE_CFG_T cfg;
        if (!button_cnt) {
            cfg.mode = TUYA_TIMER_MODE_PERIOD;
            cfg.cb = hw_timer_zll_scan_key_deal_handler;
            cfg.args = NULL;
            tkl_timer_init(TUYA_TIMER_NUM_0,&cfg);
            tkl_timer_start(TUYA_TIMER_NUM_0, 1000);
            tal_gpio_irq_disable(TUYA_GPIO_NUM_13);
            // tkl_timer_init(TUYA_TIMER_NUM_1,&cfg);
            // tkl_timer_start(TUYA_TIMER_NUM_1, 3*1000*1000);
            // tkl_timer_init(TUYA_TIMER_NUM_2,&cfg);
            // tkl_timer_start(TUYA_TIMER_NUM_2, 5*1000*1000);
            // tkl_timer_init(TUYA_TIMER_NUM_3,&cfg);
            // tkl_timer_start(TUYA_TIMER_NUM_3, 7*1000*1000);
            // tkl_timer_init(TUYA_TIMER_NUM_4,&cfg);
            // tkl_timer_start(TUYA_TIMER_NUM_4, 9*1000*1000);
            // tkl_timer_init(TUYA_TIMER_NUM_5,&cfg);
            // tkl_timer_start(TUYA_TIMER_NUM_5, 10*1000*1000);
        }
    }
#endif
}


STATIC VOID_T gpio_zll_factory_new_key_interrupt_handler(VOID_T *args)
{
    USER_PR_DEBUG("into zll_factory_new_key_interrupt\r\n");
    if (!button_cnt) {
        tal_sw_timer_start(etimer_zll_factory_new_key_check_event_id, VIBRATE_TIME, TAL_TIMER_ONCE);
    }
}

STATIC VOID_T __zll_onoff_callback(TAL_SEND_ST_E st, TAL_ZG_SEND_DATA_T *data)
{
    TAL_ZG_SEND_DATA_T send_data;

    send_data.zcl_id = 102;
    if(st == SEND_ST_OK) {
        tal_zg_clear_send_data(ZG_CLEAR_ALL_ZCL_ID, &send_data.zcl_id);
    }
}

VOID_T app_remoteControl_command(UINT16_T group_id,UINT8_T dst_ep,BOOL_T on)
{
    TAL_ZG_SEND_DATA_T send_data;
    UINT8_T i = 0;

    memset(&send_data, 0, sizeof(TAL_ZG_SEND_DATA_T));

    send_data.qos = QOS_VIP_1;//QOS_VIP_1
    send_data.zcl_id = 102;
    send_data.direction = ZG_ZCL_DATA_CLIENT_TO_SERVER;
    if(on){
        send_data.command_id = CMD_ON_COMMAND_ID;    
    }
    else{
        send_data.command_id = CMD_OFF_COMMAND_ID;    
    }
    send_data.frame_type = ZG_ZCL_FRAME_TYPE_SPEC_TO_CLUSTER;
    send_data.addr.mode = SEND_MODE_GROUP;
    send_data.addr.type.group.group_id = group_id;
    send_data.addr.type.group.src_ep = 1;
    send_data.addr.type.group.dst_ep = dst_ep;
    send_data.addr.type.group.cluster_id = CLUSTER_ON_OFF_CLUSTER_ID;
    send_data.delay_time = 0;
    send_data.random_time = 0;
    send_data.data.private.len = i;
    tal_zg_clear_send_data(ZG_CLEAR_ALL_ZCL_ID, &send_data.zcl_id);
    tal_zg_send_data(&send_data, __zll_onoff_callback, 3000);
    tal_log_print_raw("send on---\r\n");
}

/**
 * @description: gpio alarm1 debounce
 * @param [in] timer_id: timer id
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T gpio_alarm1_debounce_handler(TIMER_ID timer_id, VOID_T *arg)
{
    STATIC TUYA_GPIO_LEVEL_E alarm_state = TUYA_GPIO_LEVEL_LOW;
    STATIC BOOL_T send_on_flag = FALSE;
    tal_gpio_read(int_info->alarm.pin_num, &alarm_state);

    if (alarm_state == last_alarm_state) {
        alarm_cnt++;
    } else {
        alarm_cnt = 0;
        last_alarm_state = alarm_state;
    }

    if (alarm_cnt >= VIBRATE_CNT) {
        alarm_cnt = 0;
        if (alarm_state == (TUYA_GPIO_LEVEL_E)int_info->alarm.driver_flag) {
            if (mfg_alarm1_test_callback(1)) {
                return;
            }
            if (tkl_zg_nwk_type_get() == ZG_CENTRALIZED_NWK){
                alarm_switch_alarm1_active();
            }
            else{
                if(send_on_flag){
                    send_on_flag = FALSE;
                    app_remoteControl_command(0x4003,0xff,TRUE);                
                }
                else{
                    send_on_flag = TRUE;
                    app_remoteControl_command(0x4003,0xff,FALSE);               
                }
            }
        } else {
            if (mfg_alarm1_test_callback(0)) {
                return;
            }
            alarm_switch_alarm1_inactive();
        }
    } else {
        tal_sw_timer_start(etimer_alarm1_debounce_event_id, VIBRATE_TIME, TAL_TIMER_ONCE);
    }
}

/**
 * @description: gpio tamper debounce
 * @param [in] timer_id: timer id
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T gpio_key_debounce_handler(TIMER_ID timer_id, VOID_T *arg)
{
    TUYA_GPIO_LEVEL_E key_state = TUYA_GPIO_LEVEL_LOW;
    tal_gpio_read(int_info->key.pin_num, &key_state);
    if (TUYA_GPIO_LEVEL_HIGH == key_state) {
        gpio_key_interrupt_handler(0, 0, (button_cnt*50));
        button_cnt = 0;
        USER_PR_DEBUG("key relase\r\n");
    } else {
        gpio_key_interrupt_handler(0, 1, (button_cnt*50));
        button_cnt++;
        tal_sw_timer_start(etimer_key_debounce_event_id, 50, TAL_TIMER_ONCE);
        USER_PR_DEBUG("key press %d\r\n", button_cnt);
    }
}

/**
 * @description: gpio tamper debounce
 * @param [in] timer_id: timer id
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T gpio_zll_scan_key_deal_handler(TIMER_ID timer_id, VOID_T *arg)
{
    TUYA_GPIO_LEVEL_E key_state = TUYA_GPIO_LEVEL_LOW;
    tal_gpio_read(int_info->zll_scan_key.pin_num, &key_state);
    if (TUYA_GPIO_LEVEL_HIGH == key_state) {
        if(!scan_key_cnt){
            if(TAL_ZG_NWK_JOIN_START!=tal_zg_nwk_status_get()){
                tal_zll_initiator_scan_start(TAL_ZLL_SCANE_FOR_JOIN);
            }
        }
        scan_key_cnt++;
        USER_PR_DEBUG("key relase\r\n");
    } else {
        scan_key_cnt=0;
    }
}

/**
 * @description: gpio tamper debounce
 * @param [in] timer_id: timer id
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T hw_timer_zll_scan_key_deal_handler(VOID_T *arg)
{
    static BOOL_T onoff = FALSE;
    // USER_PR_DEBUG("hw_timer_zll_scan_key_deal_handler onoff %d\r\n",onoff);
    if (onoff) {
        onoff = FALSE;
        tal_gpio_write(TUYA_GPIO_NUM_18, TUYA_GPIO_LEVEL_HIGH);
    } else {
        onoff = TRUE;
        tal_gpio_write(TUYA_GPIO_NUM_18, TUYA_GPIO_LEVEL_LOW);
    }
    tal_gpio_irq_enable(TUYA_GPIO_NUM_13);
}


/**
 * @description: gpio tamper debounce
 * @param [in] timer_id: timer id
 * @param[in] args: null
 * @return: none
 */
STATIC VOID_T gpio_zll_factory_new_key_deal_handler(TIMER_ID timer_id, VOID_T *arg)
{
    TUYA_GPIO_LEVEL_E key_state = TUYA_GPIO_LEVEL_LOW;
    tal_gpio_read(int_info->zll_factory_new_key.pin_num, &key_state);
    if (TUYA_GPIO_LEVEL_LOW == key_state) {
        if(!factory_new_key_cnt){
            if(TAL_ZG_NWK_JOIN_START!=tal_zg_nwk_status_get()){
                tal_zll_initiator_scan_start(TAL_ZLL_SCAN_FOR_LEAVE);
            }
        }
        factory_new_key_cnt++;
        USER_PR_DEBUG("key relase\r\n");
    } else {
        factory_new_key_cnt=0;
    }
}

/**
 * @description: ias zone sensor alarm1 status active
 * @param[in] none
 * @return: none
 */
STATIC VOID_T alarm_switch_alarm1_active(VOID_T)
{
    if (is_in_alarm) {
        return;
    }
    USER_PR_DEBUG("ALARM1 trigger.\r\n");
    is_in_alarm = TRUE;
    dev_alarm_indicate(TRUE);
    ias_zone_status_bit_set_with_report(IAS_ZONE_STATUS_BIT_ALARM_1, 1, 0);
}

/**
 * @description: ias zone sensor alarm1 status inactive
 * @param[in] none
 * @return: none
 */
STATIC VOID_T alarm_switch_alarm1_inactive(VOID_T)
{
    if (!is_in_alarm) {
        return;
    }
    USER_PR_DEBUG("ALARM1 normal.\r\n");
    is_in_alarm = FALSE;
    dev_alarm_indicate(FALSE);
    ias_zone_status_bit_clear_with_report(IAS_ZONE_STATUS_BIT_ALARM_1, 1, 0);
}

/**
 * @description: net key press process
 * @param[in] time_ms: key press time in ms
 * @return: none
 */
STATIC VOID_T net_key_press_callback(UINT32_T time_ms)
{
    if (time_ms == ZIGBEE_RESET_TIME_MS) {
        tal_zg_join_start(ZIGBEE_JOIN_TIMEOUT_MS);
        USER_PR_DEBUG("net key long press 3s\r\n");
    }
    if (!key_press_flag) {
        key_press_flag = TRUE;
        button_press_on_indicate();
    }
}

/**
 * @description: long press and release time in ms
 * @param[in] time_ms: key press time in ms
 * @return: none
 */
STATIC VOID_T net_key_long_press_release_callback(UINT32_T time_ms)
{
    if (key_press_flag) {
        key_press_flag = FALSE;
        USER_PR_DEBUG("net key long press release\r\n");
        button_release_off_indicate();
    }
}

/**
 * @description: short press and release time in ms
 * @param[in] count: click times
 * @return: none
 */
STATIC VOID_T net_key_short_press_release_callback(UINT8_T count)
{
    if (key_press_flag) {
        key_press_flag = FALSE;
        USER_PR_DEBUG("net key short press release\r\n");
        button_release_off_indicate();
        app_battery_report_attribute(0x01);
    }
}


/**
 * @description: key processing
 * @param [in] key_id: the key_id of key
 * @param [in] key_st: the trigger state of key
 * @param [in] push_time: press time of key
 * @return: none
 */
STATIC VOID_T gpio_key_interrupt_handler(UINT32_T key_id, UINT8_T key_st, UINT32_T push_time)
{
    switch (key_id) {
        case 0: {
            if (key_st == 1) {
                net_key_press_callback(push_time);
            } else {
            
                if (mfg_key_test_callback(key_id)) {
                    return;
                }
                if (push_time >= KEY_PUSH_TIME_SHORT) {
                    net_key_long_press_release_callback(push_time);
                } else {
                    net_key_short_press_release_callback(0);
                }
            }
            break;
        }
        default: {
            break;
        }
    }
}

